/*
Copyright (c) 2008, Mike Gashler

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef APP_H
#define APP_H

#include <stdio.h>
#include <string.h>

// Used for parsing paths and fully-qualified filenames
struct PathData
{
	int dirStart;
	int fileStart;
	int extStart;
	int len;
};

// Throws a formatted string
void ThrowError(const char* szFormat, ...);

// Executes the specified system command. Output is directed to
// the console. (If you want to hide the output or direct it to a
// file, use SystemExecute.) I'm not sure if the child app inherits
// all of the parent's environment. Throws if there is an
// error executing the command. Otherwise, returns the exit code
// of the child process.
int SystemCall(const char* szCommand, bool wait);

// Executes the specified system command. (The application inherits
// the full environment from this app. Uses the 
// szCommand specifies both the application and the parameters.
// blocks until the process exits if (and only if) wait is true.
// output of the application is written to the files specified by
// szStdOutFilename and szStdErrFilename. These filenames can be
// NULL, in which case the output is absorbed (/dev/null).
// (If you want the output to go to the console, use SystemCall.)
// Throws if there is an error executing the command. Otherwise,
// returns the exit code of the application.
int SystemExecute(const char* szCommand, bool wait, const char* szStdOutFilename, const char* szStdErrFilename);

// Returns a filename to use for a temp file
void GetTempFilename(char* pBuf);

char* LoadFileToBuffer(const char* filename, size_t* pOutSize);
void ParseFilename(const char* szPath, struct PathData* pData);
char* NormalizeTextFile(const char* before, size_t sizeBefore, size_t* pSizeAfter);
int strlinelen(const char* str);
void BufferToHexBigEndian(FILE* pOutStream, const unsigned char* pBuffer, int nBufferSize);
unsigned char HexToByte(char msb, char lsb);
void GetAppFolder(char* buf, int bufLen, const char* appName);
void Sha512DigestFile(unsigned char* pOut64ByteHash, const char* filename);
void CondensePath(char* szPath);
int SafeStrCpy(char* szDest, const char* szSrc, int nMaxSize);

#ifndef WIN32
void DeleteFile(const char* filename);
long filelength(int filedes);
int stricmp(const char* szA, const char* szB);
int strnicmp(const char* szA, const char* szB, int len);
char* itoa(int n, char* szBuf, int b);
#endif

// A Holder will hold a pointer to allocated memory and will guarantee to
// delete it when the holder goes out of scope. This is a good way to
// guarantee to clean up memory when exceptions can be thrown by any
// function that you may call.
template <class T>
class Holder
{
private:
	T m_p;

public:
	Holder(T p)
	{
		m_p = p;
	}

	~Holder()
	{
		delete(m_p);
	}

	void Set(T p)
	{
		delete(m_p);
		m_p = p;
	}

	T Get()
	{
		return m_p;
	}

	T Drop()
	{
		T pTmp = m_p;
		m_p = NULL;
		return pTmp;
	}
};

// Just like Holder, except for arrays
template <class T>
class ArrayHolder
{
private:
	T m_p;

public:
	ArrayHolder(T p)
	{
		m_p = p;
	}

	~ArrayHolder()
	{
		delete [] m_p;
	}

	void Set(T p)
	{
		delete [] m_p;
		m_p = p;
	}

	T Get()
	{
		return m_p;
	}

	T Drop()
	{
		T pTmp = m_p;
		m_p = NULL;
		return pTmp;
	}
};

// Closes a file when this object goes out of scope
class FileHolder
{
private:
	FILE* m_pFile;

public:
	FileHolder()
	{
		m_pFile = NULL;
	}

	FileHolder(FILE* pFile)
	{
		m_pFile = pFile;
	}

	~FileHolder()
	{
		if(m_pFile)
			fclose(m_pFile);
	}

	void Set(FILE* pFile)
	{
		if(m_pFile)
		{
			if(fclose(m_pFile) != 0)
				ThrowError("failed to close file");
		}
		m_pFile = pFile;
	}

	// Returns true if successfull
	bool Close()
	{
		bool answer = true;
		if(m_pFile)
		{
			if(fclose(m_pFile) != 0)
				answer = false; // wasn't able to close it properly!
		}
		m_pFile = NULL;
		return answer;
	}

	FILE* Get()
	{
		return m_pFile;
	}

	FILE* Drop()
	{
		FILE* pFile = m_pFile;
		m_pFile = NULL;
		return pFile;
	}
};

inline int MIN(int a, int b)
{
	return a < b ? a : b;
}

inline int MAX(int a, int b)
{
	return a > b ? a : b;
}

inline unsigned short ReverseEndian(unsigned short in)
{
	unsigned short out;
	((unsigned char*)&out)[0] = ((unsigned char*)&in)[1];
	((unsigned char*)&out)[1] = ((unsigned char*)&in)[0];
	return out;
}

inline short ReverseEndian(short in)
{
	short out;
	((unsigned char*)&out)[0] = ((unsigned char*)&in)[1];
	((unsigned char*)&out)[1] = ((unsigned char*)&in)[0];
	return out;
}

inline unsigned int ReverseEndian(unsigned int in)
{
	unsigned int out;
	((unsigned char*)&out)[0] = ((unsigned char*)&in)[3];
	((unsigned char*)&out)[1] = ((unsigned char*)&in)[2];
	((unsigned char*)&out)[2] = ((unsigned char*)&in)[1];
	((unsigned char*)&out)[3] = ((unsigned char*)&in)[0];
	return out;
}

inline int ReverseEndian(int in)
{
	int out;
	((unsigned char*)&out)[0] = ((unsigned char*)&in)[3];
	((unsigned char*)&out)[1] = ((unsigned char*)&in)[2];
	((unsigned char*)&out)[2] = ((unsigned char*)&in)[1];
	((unsigned char*)&out)[3] = ((unsigned char*)&in)[0];
	return out;
}

inline float ReverseEndian(float in)
{
	float out;
	((unsigned char*)&out)[0] = ((unsigned char*)&in)[3];
	((unsigned char*)&out)[1] = ((unsigned char*)&in)[2];
	((unsigned char*)&out)[2] = ((unsigned char*)&in)[1];
	((unsigned char*)&out)[3] = ((unsigned char*)&in)[0];
	return out;
}

inline double ReverseEndian(double in)
{
	double out;
	((unsigned char*)&out)[0] = ((unsigned char*)&in)[7];
	((unsigned char*)&out)[1] = ((unsigned char*)&in)[6];
	((unsigned char*)&out)[2] = ((unsigned char*)&in)[5];
	((unsigned char*)&out)[3] = ((unsigned char*)&in)[4];
	((unsigned char*)&out)[4] = ((unsigned char*)&in)[3];
	((unsigned char*)&out)[5] = ((unsigned char*)&in)[2];
	((unsigned char*)&out)[6] = ((unsigned char*)&in)[1];
	((unsigned char*)&out)[7] = ((unsigned char*)&in)[0];
	return out;
}

// Convert little endian to a 32-bit native integer
inline unsigned int LittleEndianToN32(unsigned int in)
{
#ifdef BYTE_ORDER_BIG_ENDIAN
	return ReverseEndian(in);
#else // BYTE_ORDER_BIG_ENDIAN
	return in;
#endif // !BYTE_ORDER_BIG_ENDIAN
}

// Convert little endian to a 32-bit native integer
inline int LittleEndianToN32(int in)
{
#ifdef BYTE_ORDER_BIG_ENDIAN
	return ReverseEndian(in);
#else // BYTE_ORDER_BIG_ENDIAN
	return in;
#endif // !BYTE_ORDER_BIG_ENDIAN
}

// Convert a 32-bit native integer to little endian
inline unsigned int N32ToLittleEndian(unsigned int in)
{
#ifdef BYTE_ORDER_BIG_ENDIAN
	return ReverseEndian(in);
#else // BYTE_ORDER_BIG_ENDIAN
	return in;
#endif // !BYTE_ORDER_BIG_ENDIAN
}

// Convert a 32-bit native integer to little endian
inline int N32ToLittleEndian(int in)
{
#ifdef BYTE_ORDER_BIG_ENDIAN
	return ReverseEndian(in);
#else // BYTE_ORDER_BIG_ENDIAN
	return in;
#endif // !BYTE_ORDER_BIG_ENDIAN
}


#define MAX_STACK_TEMP_BUFFER 1024
// Helper class used by the GTEMPBUF macro
class GTempBufHelper
{
public:
	char* m_pBuf;
	GTempBufHelper(int nSize)
	{
		m_pBuf = ((nSize > MAX_STACK_TEMP_BUFFER) ? new char[nSize] : NULL);
	}

	~GTempBufHelper()
	{
		delete[] m_pBuf;
	}
};

// Macro for allocating a temporary buffer
#ifdef _DEBUG
class GTempBufSentinel
{
	void* m_pBuf;

public:
	GTempBufSentinel(void* pBuf) : m_pBuf(pBuf)
	{
		*(char*)pBuf = 'S';
	}

	~GTempBufSentinel()
	{
#ifdef _DEBUG
		if(*(char*)m_pBuf != 'S')
			ThrowError("temp buffer overrun!");
#endif
	}
};

#define GTEMPBUF(typ, var, cnt)\
	GTempBufHelper var##__(sizeof(typ) * (cnt) + 1);\
	typ* var = (((sizeof(typ) * (cnt)) <= MAX_STACK_TEMP_BUFFER) ? (typ*)alloca(sizeof(typ) * (cnt) + 1) : (typ*)var##__.m_pBuf);\
	GTempBufSentinel var##_sentinel(&var[cnt]);
#else
#define GTEMPBUF(typ, var, cnt)\
	GTempBufHelper var##__(sizeof(typ) * (cnt));\
	typ* var = (((sizeof(typ) * (cnt)) <= MAX_STACK_TEMP_BUFFER) ? (typ*)alloca(sizeof(typ) * (cnt)) : (typ*)var##__.m_pBuf);
#endif


#ifdef WIN32

#	define BAD_HANDLE (void*)1
#else // WIN32
#	define SOCKET int
#	define HANDLE unsigned long
#	define BAD_HANDLE 1
#	define INVALID_SOCKET -1
#endif // else WIN32

#endif // APP_H
